/// 所有权
/// 所有权曾经两次劝退我学rust
/*
*所有权系统，包括三个重要的组成部分：
* 所有权（Ownership）
* 借用（Borrowing）
* 生命周期（Lifetime）
*/
/**
 * 所有权规则：
 * Rust 中的每一个值都有一个被称为其所有者（owner）的变量。
 * 每个值（或者说这个值所占的内存）有且只有一个所有者。
 * 当所有者（变量）离开作用域，这个值将被丢弃（释放这个值所占的内存）。
 */
/// 绑定和作用域
/// 在 Rust 中，let 关键字用来将变量和值做“绑定”，换句话说，let 关键字将一个变量和一段内存区域关联起来。
/// 这个变量就是这段内存的所有者。
/// 作用域是一个变量在程序中有效的范围，通常是变量所在大括号{}内的范围。
/// 当一个变量离开它的作用域之后，Rust 会将该变量销毁，并将其绑定的内存资源释放。如下面的示例：
fn scope(){
    {
        let a = 5;
    }
    // 此时变量 a 及其绑定的内存已经被销毁，不能被打印
    //println!("{}",a);//这个还很好理解
}
/// 移动（Move）
/// 变量 s1 和 s2 的类型是String。String 类型数据由三部分组成。
/// 如下图左侧所示：指向存放字符串内容内存的指针、长度和容量，这一组数据存储在栈上。右侧的内容则是存放在堆上的。
/// ----------------        |index | value|
/// |name    |value|        | 0    | h    |
/// |ptr     |     |------->| 1    | e    |
/// |len     | 5   |        | 2    | l    |
/// |capacity| 5   |        | 3    | l    |
/// ----------------        | 4    | o    |
fn move_lession(){
    let s1 = String::from("hello");
    let s2 = s1;
    println!(" move to {}",s2);
    //println!("{} move to {}",s1,s2);//错误
}
/// 当我们将 s1 赋值给 s2时，s1 的数据被复制。复制的方式有两种：
/// 仅复制栈上的数据，包括指针、长度和容量（浅拷贝）。
/// 同时复制栈上和堆上的数据（深拷贝）。
fn clone(){
    let s1 = String::from("hello");
    let s2 = s1.clone();
    println!("{} move to {}",s1,s2);
}
///另外，Rust 提供了一个叫做 Copy 的特殊 Trait。
/// 如果一个数据类型实现了 Copy Trait，该类型变量在赋值给其他变量后，旧的变量仍然可用。
/// 换句话说，就是不会发生所有权转移。
fn copy(){
    let x = 5;
    let y = x;
    println!("x={}, y={}",x,y);
    //常见的拷贝类型：
    //所有整数类型，比如 u32。
    //布尔类型，bool，它的值是 true 和 false。
    //所有浮点数类型，比如 f64。
    //字符类型，char。
    //元组，当且仅当其包含的类型也都是 Copy 的时候。比如，(i32, i32) 是 Copy 的，但 (i32, String) 就不是。
    //有点像值类型和引用类型
}
/// 所有权和函数
/// 将值传递给函数在语义上与给变量赋值类似。向函数传递值可能会移动或者拷贝，就像赋值语句一样。 示例：
pub fn ownership_function(){
    let s = String::from("hello");
    takes_ownership(s); //s 的值移动到函数里
    //... 所以到这里不再有效
    let x = 5;
    makes_copy(x);      //x 应该移动函数里，
    // 但 i32 是 Copy 的，所以在后面可继续使用 x
}
fn takes_ownership(some_string:String){
    println!("{}",some_string);
}
fn makes_copy(some_integer:i32){
    println!("{}",some_integer);
}
/// 借用（borrow）
/// 不可变引用
/// 假如有一个变量在传给函数之后，我们仍然想使用它。
/// 如下面的例子，计算 s1 的长度，并输出 s1 及其长度。
/// 按照之前的规则，s1 在传给 calculate_length 函数后就失效了。
/*fn borrow(){
    let s1 = String::from("hello");
    let len = calculate_length(s1);
    println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s:String)->usize{
    s.len()
}*/
//若要重新使用 s1 的值，有两种方法：
//方法一：由 calculate_length 函数将其返回，然后再定义一个变量 s2 接收
fn borrow1() {
    let s1 = String::from("hello");
    let (s2, len) = calculate_length1(s1); // 此时 s1 已经是无效的了
    println!("The length of '{}' is {}.", s2, len);
}

fn calculate_length1(s: String) -> (String, usize) {
    let length = s.len(); // len() 返回字符串的长度
    (s, length)
}
//方法二：将 s1 拷贝一份传入函数中，这样 s1 就不会失效了
fn borrow2(){
    let s1 = String::from("hello");
    let len = calculate_length2(s1.clone());
    println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length2(s:String)->usize{
    s.len()
}
//很明显，方法一的做法非常麻烦，方法二的做法浪费内存空间。
//因此，针对这种情况 Rust 提供了一个新的功能：引用（reference）。
fn borrow3(){
    let s1 = String::from("hello");
    let len = calculate_length3(&s1);//& 符号表示这是一个引用，它允许你使用值但不获取其所有权。
    println!("The length of '{}' is {}.", s1, len);
}
//&s1 语法让我们创建一个指向 s1 所拥有值的引用，但是并不拥有它。
//同理，函数签名使用 & 来表明参数 s 的类型是一个引用。
fn calculate_length3(s:&String)->usize{
    s.len()
}
/// 可变引用
/// 默认情况下，引用的值是不允许修改的。若要想修改引用的值，需在 & 后加上 mut，如下面的例子：
fn mut_borrow(){
    let mut s = String::from("hello");
    change(&mut s);
    //可变引用有一个很大的限制：在特定作用域中的特定数据有且只有一个可变引用。
    //这个限制的好处是 Rust 可以在编译时就避免数据竞争。
}
fn change(s:&mut String){
    s.push_str(", world");
}
/// 悬垂引用
/// 在具有指针的语言中，很容易通过释放内存时保留指向它的指针而错误地生成一个悬垂指针（dangling pointer），
/// 所谓悬垂指针是其指向的内存可能已经被分配给其它持有者。
/// 在 Rust 中编译器会对引用指向的值进行检查，确保引用不会变成悬垂状态。
/*fn dangle()-> &String{
    let s = String::from("hello"); // s 是一个新字符串
    &s
}*/ // 这里 s 离开作用域并被丢弃，其内存被释放。这个引用此时指向一个无效的 String，因此编译器会报错

/**
 * 总的来说，关于引用有以下两个规则：
 * 在任意给定时间，要么只能有一个可变引用，要么只能有多个不可变引用。
 * 引用必须总是有效的。
 */

/// 切片（Slice）

/// 引用类型可以使用引用的值，但拥有该值得所有权。
/// 另一个没有所有权的数据类型是切片（Slice）。
/// 切片（Slice）用来引用集合中一段连续的元素序列，而不是引用整个集合。
/// 切片可以是共享的或者可变的，共享的切片用 &[T] 表示，可变的切片用 &mut [T] 表示。
/// 字符串切片
/// 字符串切片（string slice）是对 String 中部分连续字符的引用，如下面的例子：
fn string_slice(){
    let s = String::from("hello world");
    let hello = &s[0..5]; // hello
    let world = &s[6..11]; // world
    //切片的语法格式：[starting_index..ending_index]。
    //其中，starting_index 从 0 开始，ending_index 是最后一个位置的后一个索引。
    //在其内部，slice 的数据结构存储了 slice 的开始位置和长度，长度对应于 ending_index 减去 starting_index 的值。
    //所以切片含左不含右

    //字符串字面值其实就是切片，如下面的例子：
    //let s = "Hello, world!";
    //这里 s 的类型是 &str：它是一个指向二进制文件特定位置的切片。
    //这也就是为什么字符串字面值是不可变的，因为 &str 是一个不可变引用。

    //其它类型的切片
    //在数组中也可以使用切片，如下面的例子：
    let a = [1, 2, 3, 4, 5];
    let slice = &a[1..3];//[2,3]
    //这个切片的类型是 &[i32]。
    //它跟字符串切片的工作方式一样，通过存储第一个集合元素的引用和一个集合总长度来实现。
    //你可以对其他所有集合使用切片类型。
    //Rust 建立在所有权之上的这一套机制，
    //它要求一个资源同一时刻有且只能有一个拥有所有权的绑定或 &mut 引用，这在大部分的情况下保证了内存的安全。
}
/**
 * 生命周期
 * Rust 中的每一个引用都有其生命周期（lifetime），也就是引用保持有效的作用域。
 * 大部分时候生命周期是隐含并可以推断的，正如大部分时候类型也是可以推断的一样。
 * 类似于当因为有多种可能类型的时候必须注明类型，当 Rust 不能确定引用的生命周期时，Rust 需要我们使用泛型生命周期参数来注明。
 * 生命周期则有助于确保引用在我们需要他们的时候一直有效。
 */
///生命周期的主要目标
/// 生命周期的主要目标是避免悬垂引用。
/// Rust 编译器有一个借用检查器（borrow checker），它比较作用域来确保所有的借用（引用）都是有效的。
fn lifetime(){
    /*let r;
    {
        let x = 5;
        r = &x;
    }
    println!("{}",r);*/
    //这里将变量 r 的生命周期标记为 'a 并将变量 x 的生命周期标记为 'b。
    //编译时会报错，因为生命周期 'b 比生命周期 'a 要小：被引用的对象比它的引用者存在的时间更短。
}
/**
 * 生命周期参数
 * 对于简单的场景，Rust 编译器可以判断借用是否有效。但是在有些场景中，需要人工介入帮助 Rust 编译器进行检查。
 * 示例：定义一个 longest 函数，它返回两个字符串切片中较长的那个
 */
/*fn longest(x:&str,y:&str)->&str{
    if x.len() > y.len() {
        x
    }else{
        y
    }
    //上面的例子是不能编译通过的，因为编译器不知道该函数到底是返回 x 还是返回 y。
    //因此不能推断出返回值引用的生命周期是否大于输入参数的生命周期，也就不能保证引用的有效性。
}*/

//为了帮助编译器进行生命周期检查，Rust 提供了泛型生命周参数来定义引用之间的关系。

//生命周期参数并不改变任何引用的生命周期的长短。
//与当函数签名中指定了泛型类型参数后就可以接受任何类型一样，
//当指定了泛型生命周期后函数也能接受任何生命周期的引用。
//生命周期参数描述了多个引用生命周期相互的关系，而不影响其生命周期。
/**
 * 泛型生命周期参数语法规则：
 * 名称必须以撇号（'）开头，其名称通常全是小写，默认名称是：'a。
 * 和泛型参数一样，把它放在 <> 中，并在泛型参数之前。
 * 修饰引用类型时，放在引用符 & 之后，并后接一个空格将其与数据类型隔开。
 */
//示例一：数据类型和生命周期参数
fn foo<'a, T>() {}
trait A<U> {}
struct Ref<'a, T> where T: 'a { r: &'a T }

//示例二：给 longest 函数添加生命周期参数
// 告诉编译器，函数返回值的生命周期与两个参数的生命周期相关联。
// 实际上，编译器会取两个参数的交集，即两个参数中较小的那个生命周期作为 `'a` 的值。
// 编译器会检查返回值引用的生命周期，一旦返回值引用的生命周期大于 `'a`，则会报错。
fn longest2<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
// 为什么 `'a` 不能取较大的那个生命周期？
// 若 `'a` = 较大的生命周期，返回值既可能是 x，也可能是 y，一旦返回具有较小生命周期的参数，
// 则可能造成无效的引用（返回值引用比生命周期较小的参数引用活得长，当参数被销毁时，返回值的引用还在指向这个参数）。
// 只有`'a` 取较小的那个生命周期时，无论是返回 x，还是返回 y，才不会造成无效的引用。

//示例三：调用 longest 函数（编译通过）
fn longest3() {
    let string1 = String::from("long string is long");

    {
        let string2 = String::from("xyz");
        // `result` 的生命周期 = `string2` 的生命周期
        let result = longest2(string1.as_str(), string2.as_str());
        println!("The longest string is {}", result);
    }
}
//示例四：调用 longest 函数（编译不通过）
/*fn main() {
    let string1 = String::from("long string is long");
    let result;
    {
        let string2 = String::from("xyz");
        // `result` 的生命周期 > `string2` 的生命周期
        result = longest(string1.as_str(), string2.as_str());
    }
    println!("The longest string is {}", result);
}*/
//示例五：包含引用的结构体，也需要添加生命周期参数
// `ImportantExcerpt` 的实例不能比 `part` 字段中的引用存在更久
struct ImportantExcerpt<'a> {
    part: &'a str,
}
fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.')
        .next()
        .expect("Could not find a '.'");
    let i = ImportantExcerpt { part: first_sentence };
}
